﻿namespace PugLib.Extensions
{
    using System;
    using System.Drawing;
    using System.Drawing.Drawing2D;

    public static class GraphicsExtension
    {
        private static GraphicsPath GenerateRoundedRectangle(this RectangleF rectangle, float radius, RectangleEdgeFilter filter)
        {
            GraphicsPath path = new GraphicsPath();
            if (radius <= 0.0F)
            {
                path.AddRectangle(rectangle);
                path.CloseFigure();
                return path;
            }

            if (radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)
            {
                return rectangle.GenerateCapsule();
            }
            float diameter = radius * 2.0F;
            SizeF sizeF = new SizeF(diameter, diameter);
            RectangleF arc = new RectangleF(rectangle.Location, sizeF);
            if ((RectangleEdgeFilter.TopLeft & filter) == RectangleEdgeFilter.TopLeft)
            {
                path.AddArc(arc, 180, 90);
            }
            else
            {
                path.AddLine(arc.X, arc.Y + arc.Height, arc.X, arc.Y);
                path.AddLine(arc.X, arc.Y, arc.X + arc.Width, arc.Y);
            }
            arc.X = rectangle.Right - diameter;
            if ((RectangleEdgeFilter.TopRight & filter) ==
                RectangleEdgeFilter.TopRight)
            {
                path.AddArc(arc, 270, 90);
            }
            else
            {
                path.AddLine(arc.X, arc.Y, arc.X + arc.Width, arc.Y);
                path.AddLine(arc.X + arc.Width, arc.Y + arc.Height,
                             arc.X + arc.Width, arc.Y);
            }
            arc.Y = rectangle.Bottom - diameter;
            if ((RectangleEdgeFilter.BottomRight & filter) ==
                RectangleEdgeFilter.BottomRight)
            {
                path.AddArc(arc, 0, 90);
            }
            else
            {
                path.AddLine(arc.X + arc.Width, arc.Y, arc.X + arc.Width, arc.Y + arc.Height);
                path.AddLine(arc.X, arc.Y + arc.Height, arc.X + arc.Width, arc.Y + arc.Height);
            }
            arc.X = rectangle.Left;
            if ((RectangleEdgeFilter.BottomLeft & filter) ==
                RectangleEdgeFilter.BottomLeft)
            {
                path.AddArc(arc, 90, 90);
            }
            else
            {
                path.AddLine(arc.X + arc.Width, arc.Y + arc.Height, arc.X, arc.Y + arc.Height);
                path.AddLine(arc.X, arc.Y + arc.Height, arc.X, arc.Y);
            }
            path.CloseFigure();

            return path;
        }

        private static GraphicsPath GenerateCapsule(this RectangleF baseRect)
        {
            RectangleF arc;
            GraphicsPath path = new GraphicsPath();
            try
            {
                float diameter;
                if (baseRect.Width > baseRect.Height)
                {
                    diameter = baseRect.Height;
                    SizeF sizeF = new SizeF(diameter, diameter);
                    arc = new RectangleF(baseRect.Location, sizeF);
                    path.AddArc(arc, 90, 180);
                    arc.X = baseRect.Right - diameter;
                    path.AddArc(arc, 270, 180);
                }
                else if (baseRect.Width < baseRect.Height)
                {
                    diameter = baseRect.Width;
                    SizeF sizeF = new SizeF(diameter, diameter);
                    arc = new RectangleF(baseRect.Location, sizeF);
                    path.AddArc(arc, 180, 180);
                    arc.Y = baseRect.Bottom - diameter;
                    path.AddArc(arc, 0, 180);
                }
                else
                {
                    path.AddEllipse(baseRect);
                }
            }
            catch
            {
                path.AddEllipse(baseRect);
            }
            finally
            {
                path.CloseFigure();
            }
            return path;
        }

        public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, float x, float y, float width, float height, float radius, RectangleEdgeFilter filter)
        {
            RectangleF rectangle = new RectangleF(x, y, width, height);
            GraphicsPath path = rectangle.GenerateRoundedRectangle(radius, filter);
            SmoothingMode old = graphics.SmoothingMode;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.DrawPath(pen, path);
            graphics.SmoothingMode = old;
        }

        public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, float x, float y, float width, float height, float radius)
        {
            graphics.DrawRoundedRectangle(pen, x, y, width, height, radius, RectangleEdgeFilter.All);
        }

        public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, int x, int y, int width, int height, int radius)
        {
            graphics.DrawRoundedRectangle(pen, x, y, width, height, radius);
        }

        public static void FillRoundedRectangle(this Graphics graphics, Brush brush, float x, float y, float width, float height, float radius, RectangleEdgeFilter filter)
        {
            RectangleF rectangle = new RectangleF(x, y, width, height);
            GraphicsPath path = rectangle.GenerateRoundedRectangle(radius, filter);
            SmoothingMode old = graphics.SmoothingMode;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.FillPath(brush, path);
            graphics.SmoothingMode = old;
        }

        public static void FillRoundedRectangle(this Graphics graphics, Brush brush, float x, float y, float width, float height, float radius)
        {
            graphics.FillRoundedRectangle(brush, x, y, width, height, radius, RectangleEdgeFilter.All);
        }

        public static void FillRoundedRectangle(this Graphics graphics, Brush brush, int x, int y, int width, int height, int radius)
        {
            graphics.FillRoundedRectangle(brush, x, y, width, height, radius);
        }
    }

    [Flags]
    public enum RectangleEdgeFilter
    {
        None = 0,
        TopLeft = 1,
        TopRight = 2,
        BottomLeft = 4,
        BottomRight = 8,
        All = TopLeft | TopRight | BottomLeft | BottomRight
    }
}